// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library IndexedDB1Test;

import 'package:expect/async_helper.dart';
import 'package:expect/legacy/async_minitest.dart'; // ignore: deprecated_member_use
import 'dart:async';
import 'dart:html' as html;
import 'dart:indexed_db' as idb;
import 'dart:collection';
import 'utils.dart';

// Write and re-read Maps: simple Maps; Maps with DAGs; Maps with cycles.

const String DB_NAME = 'Test2';
const String STORE_NAME = 'TEST';
const int VERSION = 1;

testReadWrite(
  key,
  value,
  check, [
  dbName = DB_NAME,
  storeName = STORE_NAME,
  version = VERSION,
]) async {
  void createObjectStore(e) {
    idb.ObjectStore store = e.target.result.createObjectStore(storeName);
    expect(store, isNotNull);
  }

  idb.Database? db;
  // Delete any existing DBs.
  try {
    await html.window.indexedDB!.deleteDatabase(dbName);
    db = await html.window.indexedDB!.open(
      dbName,
      version: version,
      onUpgradeNeeded: createObjectStore,
    );

    idb.Transaction transaction = db.transactionList([storeName], 'readwrite');
    transaction.objectStore(storeName).put(value, key);

    db = await transaction.completed;
    transaction = db.transaction(storeName, 'readonly');
    var object = await transaction.objectStore(storeName).getObject(key);

    db.close();
    check(value, object);
  } catch (error) {
    if (db != null) {
      db.close();
    }
    throw error;
  }
}

List<String> get nonNativeListData {
  List<String> list = [];
  list.add("data");
  list.add("clone");
  list.add("error");
  list.add("test");
  return list;
}

main() {
  var obj1 = {'a': 100, 'b': 's'};
  var obj2 = {'x': obj1, 'y': obj1}; // DAG.

  var obj3 = {};
  obj3['a'] = 100;
  obj3['b'] = obj3; // Cycle.

  var obj4 = new SplayTreeMap<String, dynamic>(); // Different implementation.
  obj4['a'] = 100;
  obj4['b'] = 's';

  var cyclic_list = <Object>[1, 2, 3];
  cyclic_list[1] = cyclic_list;

  go(name, data) => testReadWrite(123, data, verifyGraph);

  test('test_verifyGraph', () {
    // Nice to know verifyGraph is working before we rely on it.
    verifyGraph(obj4, obj4);
    verifyGraph(obj1, new Map.from(obj1));
    verifyGraph(obj4, new Map.from(obj4));

    var l1 = [1, 2, 3];
    var l2 = [
      const [1, 2, 3],
      const [1, 2, 3],
    ];
    verifyGraph([l1, l1], l2);
    // Use a try-catch block, since failure can be an expect exception.
    // package:expect does not allow catching test exceptions.
    try {
      verifyGraph([
        [1, 2, 3],
        [1, 2, 3],
      ], l2);
      fail("Expected failure in verifying the graph.");
    } catch (_) {
      // Expected failure. Continue.
    }

    verifyGraph(cyclic_list, cyclic_list);
  });

  // Don't bother with these tests if it's unsupported.
  // Support is tested in indexeddb_1_test
  if (idb.IdbFactory.supported) {
    asyncTest(() async {
      await go('test_simple', obj1);
      await go('test_DAG', obj2);
      await go('test_cycle', obj3);
      await go('test_simple_splay', obj4);
      await go('const_array_1', const [
        const [1],
        const [2],
      ]);
      await go('const_array_dag', const [
        const [1],
        const [1],
      ]);
      await go('array_deferred_copy', [1, 2, 3, obj3, obj3, 6]);
      await go('array_deferred_copy_2', [
        1,
        2,
        3,
        [4, 5, obj3],
        [obj3, 6],
      ]);
      await go('cyclic_list', cyclic_list);
      await go('non-native lists', nonNativeListData);
    });
  }
}
